package security

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/click33/sa-token-go/core/adapter"
	"github.com/click33/sa-token-go/core/config"
	"github.com/click33/sa-token-go/core/token"
)

// Refresh Token Implementation
// 刷新令牌实现
//
// Flow | 流程:
// 1. GenerateTokenPair() - Create access token + refresh token | 创建访问令牌 + 刷新令牌
// 2. Access token expires (short-lived, e.g. 2h) | 访问令牌过期（短期，如2小时）
// 3. RefreshAccessToken() - Use refresh token to get new access token | 使用刷新令牌获取新访问令牌
// 4. Refresh token expires (long-lived, 30 days) | 刷新令牌过期（长期，30天）
//
// Usage | 用法:
//   tokenInfo, _ := manager.LoginWithRefreshToken(loginID, "web")
//   // ... access token expires ...
//   newInfo, _ := manager.RefreshAccessToken(tokenInfo.RefreshToken)

// RefreshTokenInfo refresh token information | 刷新令牌信息
type RefreshTokenInfo struct {
	RefreshToken string // Refresh token (long-lived) | 刷新令牌（长期有效）
	AccessToken  string // Access token (short-lived) | 访问令牌（短期有效）
	LoginID      string // User login ID | 用户登录ID
	Device       string // Device type | 设备类型
	CreateTime   int64  // Creation timestamp | 创建时间戳
	ExpireTime   int64  // Expiration timestamp | 过期时间戳
}

// RefreshTokenManager refresh token manager | 刷新令牌管理器
type RefreshTokenManager struct {
	storage    adapter.Storage
	tokenGen   *token.Generator
	refreshTTL time.Duration // Refresh token TTL (30 days) | 刷新令牌有效期（30天）
	accessTTL  time.Duration // Access token TTL (configurable) | 访问令牌有效期（可配置）
}

// NewRefreshTokenManager creates a new refresh token manager | 创建新的刷新令牌管理器
// cfg: configuration, uses Timeout for access token TTL | 配置，使用Timeout作为访问令牌有效期
func NewRefreshTokenManager(storage adapter.Storage, cfg *config.Config) *RefreshTokenManager {
	refreshTTL := 30 * 24 * time.Hour // 30 days | 30天
	accessTTL := time.Duration(cfg.Timeout) * time.Second

	if accessTTL == 0 {
		accessTTL = 2 * time.Hour // Default 2 hours | 默认2小时
	}

	return &RefreshTokenManager{
		storage:    storage,
		tokenGen:   token.NewGenerator(cfg),
		refreshTTL: refreshTTL,
		accessTTL:  accessTTL,
	}
}

// GenerateTokenPair generates access token and refresh token pair | 生成访问令牌和刷新令牌对
func (rtm *RefreshTokenManager) GenerateTokenPair(loginID, device string) (*RefreshTokenInfo, error) {
	accessToken, err := rtm.tokenGen.Generate(loginID, device)
	if err != nil {
		return nil, err
	}

	refreshTokenBytes := make([]byte, 32)
	if _, err := rand.Read(refreshTokenBytes); err != nil {
		return nil, err
	}
	refreshToken := hex.EncodeToString(refreshTokenBytes)

	now := time.Now()
	info := &RefreshTokenInfo{
		RefreshToken: refreshToken,
		AccessToken:  accessToken,
		LoginID:      loginID,
		Device:       device,
		CreateTime:   now.Unix(),
		ExpireTime:   now.Add(rtm.refreshTTL).Unix(),
	}

	key := fmt.Sprintf("satoken:refresh:%s", refreshToken)
	if err := rtm.storage.Set(key, info, rtm.refreshTTL); err != nil {
		return nil, err
	}

	return info, nil
}

// RefreshAccessToken generates new access token using refresh token | 使用刷新令牌生成新的访问令牌
func (rtm *RefreshTokenManager) RefreshAccessToken(refreshToken string) (*RefreshTokenInfo, error) {
	key := fmt.Sprintf("satoken:refresh:%s", refreshToken)

	data, err := rtm.storage.Get(key)
	if err != nil {
		return nil, fmt.Errorf("invalid refresh token")
	}

	oldInfo, ok := data.(*RefreshTokenInfo)
	if !ok {
		return nil, fmt.Errorf("invalid refresh token data")
	}

	if time.Now().Unix() > oldInfo.ExpireTime {
		rtm.storage.Delete(key)
		return nil, fmt.Errorf("refresh token expired")
	}

	newAccessToken, err := rtm.tokenGen.Generate(oldInfo.LoginID, oldInfo.Device)
	if err != nil {
		return nil, err
	}

	oldInfo.AccessToken = newAccessToken

	if err := rtm.storage.Set(key, oldInfo, rtm.refreshTTL); err != nil {
		return nil, err
	}

	return oldInfo, nil
}

// RevokeRefreshToken revokes a refresh token | 撤销刷新令牌
func (rtm *RefreshTokenManager) RevokeRefreshToken(refreshToken string) error {
	key := fmt.Sprintf("satoken:refresh:%s", refreshToken)
	return rtm.storage.Delete(key)
}

// GetRefreshTokenInfo gets refresh token information | 获取刷新令牌信息
func (rtm *RefreshTokenManager) GetRefreshTokenInfo(refreshToken string) (*RefreshTokenInfo, error) {
	key := fmt.Sprintf("satoken:refresh:%s", refreshToken)

	data, err := rtm.storage.Get(key)
	if err != nil {
		return nil, err
	}

	info, ok := data.(*RefreshTokenInfo)
	if !ok {
		return nil, fmt.Errorf("invalid refresh token data")
	}

	return info, nil
}
